﻿

using Admin.NET.Core;
using AngleSharp.Dom;
using FluentEmail.Core;
using FluentEmail.Core.Models;
using Flurl.Util;
using Furion;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;

using IotContract.Enums;
using IotContract.Models.Clients;
using IotFun.Const;
using IotFun.Entity;
using IotFun.Services;
using IotFun.Views.EquipInfoView.Dto;
using Magicodes.ExporterAndImporter.Core;
using Magicodes.ExporterAndImporter.Core.Models;
using Magicodes.ExporterAndImporter.Excel;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Nest;
using Newtonsoft.Json;
using SkiaSharp;
using System.Composition;
using System.Text;
using System.Text.RegularExpressions;
using XT.Common.Extensions;

namespace IotFun.Views.EquipInfoView;
/// <summary>
/// 设备管理服务
/// </summary>
[ApiDescriptionSettings(IotConst.GroupName)]
public class EquipInfoService : IDynamicApiController, ITransient
{
    private readonly SqlSugarRepository<EquipInfo> _equipInfoRep;
    private readonly SqlSugarRepository<ProgramInfo> _programInfoRep;
    private readonly SqlSugarRepository<EquipGroup> _equipGroupRep;
    private readonly GrpcService _grpcService;
    public EquipInfoService(SqlSugarRepository<EquipInfo> equipInfoRep, SqlSugarRepository<ProgramInfo> programInfoRep, SqlSugarRepository<EquipGroup> equipGroupRep, GrpcService grpcService)
    {
        _equipInfoRep = equipInfoRep;
        _programInfoRep = programInfoRep;
        _equipGroupRep = equipGroupRep;
        _grpcService = grpcService;
    }
    /// <summary>
    /// 根据设备编码获取设备id
    /// </summary>
    /// <param name="num"></param>
    /// <returns></returns>
    public async Task<long> GetIdByNum(string num)
    {
       return await _equipInfoRep.AsQueryable().Where(x => x.Num == num).Select(x=>x.Id).FirstAsync();
    }
   
    /// <summary>
    /// 构造点位数据
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "GenerateTag")]
    public async Task GenerateTags(EquipGenerateInput input)
    {
        string maxNum = "";
        if (input.CopyTagNum.IsNotNullOrEmpty())
        {
            maxNum = input.CopyTagNum;
        }
        else
        {
            maxNum = await _equipInfoRep.Context.Queryable<EquipTag>().Where(x => x.EquipInfoId == input.EquipId).MaxAsync(x => x.TagNum);
          
        }

        if (maxNum == input.TagNum)
        {
            throw Oops.Oh("tagNum is exist");
        }

        var tags= await _equipInfoRep.Context.Queryable<EquipTag>().Where(x => x.TagNum == maxNum && x.EquipInfoId==input.EquipId).OrderBy(x=>x.Address).ToListAsync();

        tags.ForEach(x =>
        {
            x.Id = 0;
            if (x.ReadWrite == 1)
            {
                x.Address = (x.Address.To<int>() + input.WriteStep).ToString();
            }
            else
            {
                x.Address = (x.Address.To<int>() + input.ReadStep).ToString();
            }
           
           
            x.TagNum = input.TagNum;
        });

       await _equipGroupRep.Context.Insertable<EquipTag>(tags).ExecuteCommandAsync();
      
    }


    /// <summary>
    /// 批量构造点位数据
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "GenerateAllTag")]
    public async Task GenerateAllTags(EquipGenerateInput input)
    {
       var nums= input.TagNum.Split(',');

        foreach(var num in nums)
        {
            input.TagNum = num;
            await GenerateTags(input);
        }

    }

    /// <summary>
    /// 根据序号构造点位数据
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "GenerateTagsByIndex")]
    public async Task GenerateTagsByIndex(EquipGenerateIndex input)
    {
        for(int i = input.FromTagNum; i <= input.ToTagNum; i++)
        {
            EquipGenerateInput data = new EquipGenerateInput
            {
                EquipId = input.EquipId,
                ReadStep = input.ReadStep,
                TagNum = i.ToString(),
                WriteStep = input.WriteStep,
            };
            await GenerateTags(data);
        }     
    }

    /// <summary>
    /// 根据序号删除点位数据
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "DeleteTagsByIndex")]
    public async Task DeleteTagsByIndex(EquipGenerateInput input)
    {
        var nums = input.TagNum.Split(',');

        foreach (var num in nums)
        {
            await _equipInfoRep.Context.Deleteable<EquipTag>().Where(x => x.EquipInfoId == input.EquipId && x.TagNum == num).ExecuteCommandAsync();
        }

    }
    /// <summary>
    /// 构造设备数据
    /// </summary>
    /// <param name="tagNum"></param>
    /// <param name="equipId"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "GenerateEquip")]
    [UnitOfWork]
    public async Task GenerateEquip(EquipGenerateInput input)
    {
      var equip= await _equipInfoRep.GetByIdAsync(input.EquipId);
        equip.Id = 0;
        equip.Name = equip.Name + "_Copy";
      var id= await _equipInfoRep.InsertReturnSnowflakeIdAsync(equip);
        var tags = await _equipInfoRep.Context.Queryable<EquipTag>().Where(x =>  x.EquipInfoId == input.EquipId).OrderBy(x => x.Address).ToListAsync();

        if(tags!=null && tags.Count > 0)
        {
            var first = tags.First();
         

            tags.ForEach(x =>
            {
                x.EquipInfoId = id;
                x.Id = 0;
            
              
                
            });
            await _equipInfoRep.Context.Insertable<EquipTag>(tags).ExecuteCommandAsync();
        }


    }

    /// <summary>
    /// 分页查询设备管理
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Page")]
    public async Task<SqlSugarPagedList<EquipInfoOutput>> Page([FromQuery] EquipInfoInput input)
    {
        
        var query = _equipInfoRep.AsQueryable()
                    .WhereIF(input.isEnable!=null,u=>u.IsEnable==input.isEnable)
                    .WhereIF(!string.IsNullOrWhiteSpace(input.equiptype), u => u.EquipType == input.equiptype)
                     .WhereIF(!string.IsNullOrWhiteSpace(input.protobuftype), u => u.ProtobufType == input.protobuftype)
                    .LeftJoin<EquipGroup>((x, y) => x.GroupId == y.Id).LeftJoin<ProgramInfo>((x, y, z) => x.ProgramId == z.Id).
                    WhereIF(input.programid > 0, (x, y, z) => z.Id == input.programid)
                    .WhereIF(!string.IsNullOrWhiteSpace(input.name), (x, y, z) => x.Name.Contains(input.name.Trim()) || x.Num.Contains(input.name.Trim()) || y.Name.Contains(input.name.Trim()) || x.UnitNum == input.name.Trim() || x.BindNum == input.name.Trim())
                    .Where(x => x.IsDelete == false)
                    .Select((x, y, z) => new EquipInfoOutput
                    {
                        id = x.Id,
                        remark = x.Remark,
                        groupname = y.Name,
                        name = x.Name,
                        programname = z.Name,
                        ismqttpublish = x.IsMqttPublish
                    }, true);

         var r =await query.ToPagedListAsync(input.Page, input.PageSize);
        return r;
    }

    /// <summary>
    /// 增加设备管理
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Add")]
    public async Task Add(AddEquipInfoInput input)
    {
        var entity = input.Adapt<EquipInfo>();
        await _equipInfoRep.InsertAsync(entity);
    }
    /// <summary>
    /// 根据分组变更程序
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "changeProgramByGroup")]
    public async Task ChangeProgramByGroup(EquipProgramChangeInput input)
    {
        if (input.Action == 3)
        {
            await _equipInfoRep.AsUpdateable().Where(x => x.GroupId == input.GroupId).SetColumns(x => x.ProgramId == input.ProgramId).ExecuteCommandAsync();
        }
        else
        {



            var equips = await _equipInfoRep.AsQueryable().Where(x => x.GroupId == input.GroupId && x.IsEnable==1).Select(x => new EquipInfo { Id = x.Id, ProgramId = x.ProgramId,ProtobufType=x.ProtobufType }).ToListAsync();

            if (equips != null && equips.Count > 0)
            {
                List<ClientControlCommand> commands = new List<ClientControlCommand>();
                foreach (var equip in equips)
                {
                    commands.Add(new ClientControlCommand
                    {
                        ProgramId = equip.ProgramId,
                        EquipId = equip.Id,
                        ControlType = input.Action == 1 ? ProgramControlType.Connect : ProgramControlType.DisConnect,
                        ProtobufType=equip.ProtobufType
                        
                    });
                }
                await _grpcService.ControlCommand(commands);
            }
        }
       
    }

    /// <summary>
    /// 删除设备管理
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Delete")]
    [UnitOfWork]
    public async Task Delete(DeleteEquipInfoInput input)
    {
        var entity = await _equipInfoRep.GetFirstAsync(u => u.Id == input.id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
        
        await _equipInfoRep.DeleteAsync(entity);   
       await _equipGroupRep.Context.Deleteable<EquipTag>().Where(x => x.EquipInfoId == input.id).ExecuteCommandAsync();
    }

    /// <summary>
    /// 更新设备管理
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "Update")]
    public async Task Update(UpdateEquipInfoInput input)
    {
        var entity = input.Adapt<EquipInfo>();
        await _equipInfoRep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
        //_grpcService.SetEquipMqttStatus(entity.ProtobufType,entity.Id,entity.IsMqttPublish);
    }

    /// <summary>
    /// 获取设备管理
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Detail")]
    public async Task<EquipInfo> Get([FromQuery] QueryByIdEquipInfoInput input)
    {
        return await _equipInfoRep.GetFirstAsync(u => u.Id == input.id);
    }

    /// <summary>
    /// 获取设备管理列表
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "List")]
    public async Task<List<EquipInfoOutput>> List([FromQuery] EquipInfoInput input)
    {
        return await _equipInfoRep.AsQueryable().Select<EquipInfoOutput>().ToListAsync();
    }
    /// <summary>
    /// 获取设备类型
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "Type")]
    public async Task<dynamic> GetEquipTypes()
    {
        var datas= IotEquipType.CV.ToKeyDescriptionList();

        var programs = IotProgramType.IotServer.ToKeyDescriptionList();

        programs.AddRange(datas);
        return programs; 
    }



    /// <summary>
    /// 获取协议类型
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "ProtoType")]
    public async Task<dynamic> GetProtobufTypes()
    {
        var datas= IotProtobufType.HBC_BCR.ToKeyDescriptionList();
        return datas;
    }

    /// <summary>
    /// 下载设备管理导入模板
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "exportTemplate")]
    public async Task<string> ExportEquipInfoTemplate()
    {
        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "设备管理数据导入模板.xlsx");
        if (!File.Exists(filePath))
        {
            throw Oops.Oh("未配置模板，无法导出");
        }
        return new FileInfo(filePath).Name;
    }
    /// <summary>
    /// 全部导出设备管理
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "export")]
    public async Task<string> ExportEquipInfo([FromQuery] EquipInfoInput input)
    {
        var query = _equipInfoRep.AsQueryable()
                     .WhereIF(!string.IsNullOrWhiteSpace(input.equiptype), u => u.EquipType == input.equiptype)
                      .WhereIF(!string.IsNullOrWhiteSpace(input.protobuftype), u => u.ProtobufType == input.protobuftype)
                     .LeftJoin<EquipGroup>((x, y) => x.GroupId == y.Id).LeftJoin<ProgramInfo>((x, y, z) => x.ProgramId == z.Id).
                     WhereIF(input.programid > 0, (x, y, z) => z.Id == input.programid)
                     .WhereIF(!string.IsNullOrWhiteSpace(input.name), (x, y, z) => x.Name.Contains(input.name.Trim()) || x.Num.Contains(input.name.Trim()) || y.Name.Contains(input.name.Trim()) || x.UnitNum == input.name.Trim() || x.BindNum == input.name.Trim())
                     .Where(x => x.IsDelete == false)
                     .Select((x, y, z) => new EquipInfoOutput
                     {
                         id = x.Id,
                         remark = x.Remark,
                         groupname = y.Name,
                         name = x.Name,
                         programname = z.Name,
                         ismqttpublish = x.IsMqttPublish
                     }, true);

        List<EquipInfoOutput> datas = await query.ToListAsync();

        if (datas.Count == 0)
        {
            throw Oops.Oh("没有数据，无法导出");
        }
        var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, "设备管理导出.xlsx");

        if (File.Exists(filePath)) File.Delete(filePath);

        var exports = datas.Adapt<List<EquipInfoOutput>>();

        var result = await new ExcelExporter().Export(filePath, exports);

        return new FileInfo(filePath).Name;
    }

    /// <summary>
    /// 从模板导入设备管理
    /// </summary>
    /// <param name="formCollection">前端上传的文件集合</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "import")]
    public  async Task<string> Upload([FromForm] IFormCollection formCollection)
    {
        if (formCollection == null || formCollection.Files.Count <= 0 || formCollection.Files.Count <= 0)
        {
            throw Oops.Oh("导入异常，检查导入文件数据");
        }
        var fileCollection = (FormFileCollection)formCollection.Files;
        foreach (IFormFile file in fileCollection)
        {
            ImportResult<EquipInfoImport> importRes;
            ExcelImporter excelImporter = new ExcelImporter();
           
            using (Stream memory = new System.IO.MemoryStream())
            {
                file.CopyTo(memory);
                importRes = await excelImporter.Import<EquipInfoImport>(memory);
            }
            //导入校验
            if (!importRes.IsNotNull() || importRes.Exception != null )
            {
                throw Oops.Oh("导入异常，检查导入数据");
            }
            else if (importRes.RowErrors.Count > 0 || importRes.HasError)
            {
                StringBuilder rowErrors = new StringBuilder();
                for (int i = 0; i < importRes.RowErrors.Count; i++)
                {
                    rowErrors.Append("第"+ importRes.RowErrors[i].RowIndex + "行，");
                    foreach (var headerName in importRes.RowErrors[i].FieldErrors)
                    {
                        rowErrors.Append(headerName.Key + ",");
                    }
                    rowErrors.Append("数据格式错误！");
                }
                throw Oops.Oh(rowErrors.ToString());
            }
            else//数据校验及导入
            {
                List<EquipInfo> list =new List<EquipInfo>();
                StringBuilder exceptions = new StringBuilder();
                for (int i = 0; i< importRes.Data.Count; i++)
                {
                    
                        EquipInfoImport item = importRes.Data.ToList()[i];
                        StringBuilder ex = new StringBuilder();
                        var equipInfo = new EquipInfo();

                        //检查是否定义合法的程序
                        var resp = _programInfoRep.AsQueryable()
                        .WhereIF(!string.IsNullOrWhiteSpace(item.programname), x => x.Name == item.programname).ToList();
                        //KeyValuePair<string,List<SqlSugar.SugarParameter>> s = resp.ToSql();
                        //var res = resp.ToList();

                        if (resp.Count <= 0)
                        {
                            ex.Append("未设置正确的程序名称，");
                        }
                        //不为空时验证分组
                        if (item.groupname.IsNotNullOrEmpty())
                        {
                            var equipGroupResp = _equipGroupRep.AsQueryable().Where(x => x.Name == item.groupname).ToList();
                            if (equipGroupResp.Count > 0)
                            {
                                equipInfo.GroupId = equipGroupResp[0].Id;
                            }
                            else
                            {
                                ex.Append("未设置正确的分组，");
                            }
                        }
                        //验证设备类型
                        if (!Enum.IsDefined(typeof(IotEquipType), item.equiptype))
                        {
                            ex.Append("未设置正确的设备类型，");
                        }
                        //验证协议类型
                        if (!Enum.IsDefined(typeof(IotProtobufType), item.protobuftype))
                        {
                            ex.Append("未设置正确的协议类型，");
                        }

                        
                        //验证IP
                        //if (!Regex.IsMatch(item.ipaddress, @"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{2,5}$"))
                        //{
                        //    ex.Append("未设置正确的IP和端口，");
                        //}

                        //验证循环时间
                        int aa;
                        if (!int.TryParse(item.pooling.ToString(), out aa))
                        {
                            ex.Append("未设置正确的循环时间，");
                        }
                        //验证编号，设备（PLC）编号不能重复 
                       
                            var res = _equipInfoRep.AsQueryable().Where(x => x.Num == item.num && x.ProtobufType==item.protobuftype).ToList();
                            if (res.Count > 0)
                            {
                                ex.Append(" 编号已存在，不能重复，");
                            }
                      

                        if (ex.Length > 0)
                        {
                            string exStr = ex.ToString();
                            exceptions.Append("第" + (i + 2) + "行，" + item.name + exStr.Substring(0, exStr.Length - 1) + "；");
                            continue;
                        }
                        equipInfo.Name = item.name;
                        equipInfo.ProgramId = resp[0].Id;
                        equipInfo.EquipType = item.equiptype;
                        equipInfo.ProtobufType = item.protobuftype;
                        equipInfo.Num = item.num;
                        equipInfo.OrderNum = item.ordernum;
                        equipInfo.IPAddress = item.ipaddress;
                        equipInfo.ReadDirection = item.readdirection;
                        equipInfo.BindNum = item.bindnum;
                        equipInfo.Remark = item.remark;
                        equipInfo.UnitNum = item.unitnum;
                        equipInfo.IsEnable = item.isenable;
                        equipInfo.Pooling = item.pooling;

                        list.Add(equipInfo);
                    
                }
                if(list.Count > 0 && exceptions.Length == 0)
                {
                    //SQLSugar写入数据
                    await _equipInfoRep.InsertRangeAsync(list);
                    return "导入完成！";
                }
                throw Oops.Oh("导入异常！" + exceptions);
                


            }
        }
        throw Oops.Oh("导入异常！");
    }
}

